import { ICatchConfig, ICatchDetailConfig } from "../lib/config";
import { getBookChapters, getBookContent, getHostFromUrl, IChapter, mkdirs, webRequest } from "../lib/RequestUtil";
import * as path from 'path';
import * as fs from "fs";

export abstract class RequestGroupBase {
    name = '';
    url: string = '';
    searchStart: string = '';
    searchEnd: string = '';
    orderAsc: boolean = true;
    titleRegExp: RegExp = /./;
}

export class RequestBookHandler {
    private allChapter: IChapter[] = [];
    private curLoadingIndex = 0;

    public loadingProcessEvent: (curLoadingIndex: number, total: number) => void = () => { };

    constructor(private groupConfig: ICatchConfig, private detailConfig: ICatchDetailConfig, private extensionPath: string) {

    }

    public loadData() {
        this.saveDataLoadingProcessAsync().then(result => {
            this.loadRemotePageData();
        });
    }

    private async loadRemotePageData(): Promise<unknown> {
        this.loadingProcessEvent(this.curLoadingIndex, this.allChapter.length);
        let curItem = this.allChapter[this.curLoadingIndex];
        if (!curItem ?.link) {
            return;
        }

        if (this.isContentExists(curItem.sortId!)) {
            this.curLoadingIndex++;
            this.loadRemotePageData();
        }
        else {
            let total = 5;

            while (total) {
                const hasSuccess = await this.loadRemotePageDataItemAsync(curItem).catch(err => {
                    console.error(err)
                });
                if (!hasSuccess) {
                    total--;
                    await this.sleep(3 * 1000);
                } else {
                    break;
                }
            }

            if (total < 0) {
                // 五次重试不成功
                return
            }

            this.curLoadingIndex++;
            this.loadRemotePageData();
        }

        return
    }

    private loadRemotePageDataItemAsync(curItem: IChapter): Promise<boolean> {
        return new Promise((resovle, reject) => {
            webRequest(curItem.link).then(content => {
                let text = getBookContent(content, this.groupConfig.detailRegex);
                this.saveContent(curItem.sortId!.toString(), text);
                resovle(true);
            })
        });
    }

    private sleep(time: number): Promise<void> {
        return new Promise((resovle) => {
            setTimeout(() => {
                resovle();
            }, time);
        });
    }

    private saveDataLoadingProcessAsync(): Promise<IChapter[]> {
        let arr = this.detailConfig.urls.split('\n').map(a => a.trim()).filter(a => a);
        let arrPromise = arr.map(a => this.loadBookItemAsync(a));

        return new Promise((resovle, reject) => {
            Promise.all(arrPromise).then(results => {
                let list: IChapter[] = [];
                results.forEach(a => list.push(...a));
                this.saveProcessJson(list);
                this.allChapter = list;
                resovle(list);
            });
        });
    }

    private loadBookItemAsync(bookPageUrl: string): Promise<IChapter[]> {
        return new Promise((resovle, reject) => {
            webRequest(bookPageUrl).then(content => {
                let chapters = getBookChapters(bookPageUrl, content, this.groupConfig.start, this.groupConfig.end, this.groupConfig.regex);
                resovle(chapters);
            });
        });
    }

    private saveProcessJson(data: IChapter[]) {
        data.forEach((item, index) => item.sortId = index + 1);
        let jsonPath = this.getProcessJsonPath();
        let dir = path.dirname(jsonPath);
        mkdirs(dir);
        fs.writeFileSync(jsonPath, JSON.stringify(data, undefined, 2), { encoding: 'utf-8' });
    }

    public getProcessJsonPath() {
        let str = `${this.extensionPath}/catch/data/${this.detailConfig.name}/data.json`;
        return str;
    }

    public getProcessJson(): IChapter[] {
        let filePath = this.getProcessJsonPath();
        if (!fs.existsSync(filePath)) {
            return [];
        }
        let jsonStr = fs.readFileSync(filePath, { encoding: 'utf-8' });
        let data = JSON.parse(jsonStr);
        return data;
    }

    public getContentDir() {
        let str = `${this.extensionPath}/catch/data/${this.detailConfig.name}/content`;
        return str;
    }

    private saveContent(name: string, content: string) {
        let dir = this.getContentDir();
        mkdirs(dir);

        let filePath = dir + '/' + name + '.txt';
        fs.writeFileSync(filePath, content, { encoding: 'utf-8' });
    }

    private isContentExists(name: string | number): boolean {
        let filePath = this.getContentDir() + '/' + name + '.txt';
        return fs.existsSync(filePath);
    }
}